package demo.test;
import java.awt.* ;
import java.awt.event.* ;


/**
 * A <em>DemoPane</em> is an object that displays objects having a graphical 
 * representation.
 * DemoPane ��ʾ�������е� DisplayUnit �������ʵ���˶�DisplayUnit�����
 * ���е��ϷŲ���������DisplayUnit ���ʹ��addUnit(DisplayUnit) ������
 * ÿһ�����DisplayUnit Ӧ���������Ͻ���꣨x,y���Ϳ?�ߣ�width,height����
 * ���� isMovable() ����Ϊtrue �� DisplayUnit ������������ķ�Χ���϶���ʹ
 * �÷��� setDragBounds(x,y,width,height) ���趨�϶���Χ ��
 */
public class DemoPane extends Panel implements 
					MouseMotionListener, MouseListener{
	
	public static final int WAIT=0 , PRESSED=1 ,DRAG=2 ;
	
	int status = DemoPane.WAIT ;
	
    /**
     * The number of units in this pane.
	   * This value can be null.
	   * @serial
     * @see getUnit()
     * @see getUnits()
     */
    int nunits;

	private int lastX,lastY;
	private int pressedX, pressedY ;
	
	public int unitX,unitY; //mouse position buffer
	public int bgWidth,bgHeight;
	DisplayUnit units[]=new DisplayUnit[4] ;
	DisplayUnit activeUnit ;
	int activeIndex=-1, lastActIndex=-1 ;
	
	Rectangle dragBounds ;
	
	/**
	  * the background image for this pane.
	*/
	Image backImage ;
	/**
	 * the buffer image for display
	 */
	Image bufImage ;

	Color bgColor = new Color(0xffffff) ;
	
	private boolean mouseActionEnabled ;
	
	protected boolean fresh = false ;
	
	public DemoPane() {
		this(0,0) ;		
	}
	
	public DemoPane(int w , int h ){
		super() ;
		setLayout(null) ;
		enableMouseAction(true) ;
		setSize(w,h) ;
	}
	
	/**
	 *	return the number of displayUnit in this pane.
	 */
	public int getUnitNum() {
		return nunits ;
	}
	/**
	 * setSize ִ�к󣬻������趨 dragBounds Ϊ�µ�getBounds() ��Χ
	 * ������ dragBounds ���� DemoPane ��ȫ������ִ��setSize(w,h)
	 * ��Ӧ�������� dragBounds��
	 * @see setDragBounds
	 */
	public void setSize(int w, int h) {
		super.setSize(w,h) ;
		setDragBounds(new Rectangle(0,0,w,h) ) ;
	}
	/**
	 * set the bounds for dragging operation.
	 * movable units in the dragBounds can be moved.
	 */
	public void setDragBounds( Rectangle bounds) {
		dragBounds = bounds ;
	}
	
	public void setBgImage(Image img) {
		backImage = img ;
	}
	
	public void enableMouseAction(boolean enable) {
		if (enable) {
			if ( !mouseActionEnabled) {
			addMouseListener(this); 
				addMouseMotionListener(this) ;
			}
		} else {
		removeMouseListener(this); 
		removeMouseMotionListener(this) ;
		}
		mouseActionEnabled = enable ;		
	}
	
	public void addUnit(DisplayUnit du) {
		addUnit(du,-1) ;
	}		
	
	public void addUnit(DisplayUnit du, int index ) {
		if (nunits == units.length) {
			DisplayUnit newunits[] = new DisplayUnit[nunits * 2];
			System.arraycopy(units, 0, newunits, 0, nunits);
			units = newunits;
		}
	    if (index == -1 || index == nunits) {
		units[nunits++] = du;
	    } else {
		System.arraycopy(units, index, units,
				 index + 1, nunits - index);
		units[index] = du;
		nunits++;
	    }

		du.setParent(this) ;
		add(du) ;
	}
	
   /** 
     * Removes the units, specified by <code>index</code>, 
     * from this container. 
     * @param     index   the index of the units to be removed.
     * @see #addUnit
     */
    public void removeUnit(int index) {
    		if ( (index<0)|| (index>nunits) ) return ;
    		
    	    DisplayUnit comp = units[index];
	    comp.parent = null;
	    
	    System.arraycopy(units, index + 1,
			     units, index,
			     nunits - index - 1);
	    units[--nunits] = null;
		super.remove(comp) ;
	    return;
    }
    
    public void removeUnit( DisplayUnit comp) {
    		for ( int i=0  ; i<nunits ; i++ ) {
    			if ( comp == units[i] ) {
    				removeUnit(i) ;
    				break ;
    			} 
    		}
    		return ;
    }


	public DisplayUnit getUnit(int index) {
		if ( (index<0) || (index>=nunits) )
			return null ;
		else 
			return units[index] ;
	}
	
	public int getActiveIndex() {
		return activeIndex ;
	}
	
	protected Image prepareBufImage() {
		boolean redraw=false ;
		int width = getSize().width ;
		int height = getSize().height ;

		if ( fresh ) {
			redraw=true ;
			fresh = false ;
		}
		if ( (bufImage==null) ||
				(bufImage.getWidth(null)!=width) ||
			 	(bufImage.getHeight(null)!=height) ){
			bufImage = createImage(width,height) ;
			redraw =true ;
		}
		if (lastActIndex!=activeIndex) {
			redraw=true ;
			lastActIndex = activeIndex ;
		}
		if (redraw) {
			Graphics g = bufImage.getGraphics() ;
			g.clearRect(0,0,width,height) ;
			if (backImage!=null) 
				g.drawImage(backImage, 0,0,width,height,this) ;
			for ( int i=0 ; i<nunits ; i++ ) {
				if (i!=activeIndex) {
					getUnit(i).paint(g) ;
				}
			}
		}
		return bufImage ;
	}
		
	/**
	 * if _fresh is true, the buffered image will be surely freshed
	 * ǿ��ˢ��ʹ�� repaint(true) �������ʹΪ�϶��Ż����Ĳ���Ҳ��
	 * �ػ档
	 */
	public void repaint(boolean _fresh) {
		fresh = _fresh ;
		repaint() ;
	}
	
	public void update(Graphics g) {
		paint(g) ;
	}
	public void paint(Graphics g) {
		
		prepareBufImage() ;
		g.drawImage(bufImage, 0,0,getSize().width,getSize().height,this) ;
//		g.drawImage(backImage,0,0,backImage.getWidth(null),backImage.getHeight(null),this) ;
		bgWidth=bufImage.getWidth(this);
		bgHeight=bufImage.getHeight(this);
		DisplayUnit u = getUnit(activeIndex) ;
		if (u!=null) {
			u.paint(g) ;
		}
	
	}

	/** 
	 * �˷�����activeIndex��Ϊ-1������reset�������罫DisplayUnit����
	 * ��ʼλ�õ�����д�˷�����������ɡ�
	 */
	public void reset() {
		lastActIndex = activeIndex ;
		activeIndex=-1 ;
	}
	
	public void mousePressed(MouseEvent me){
		int x = me.getX() , y = me.getY() ;
		pressedX = x ; pressedY = y ;
		if ( activeIndex!=-1) {
			if ( getUnit(activeIndex).inSelectArea(x,y) ) { 
					// make sure the last moved object has the highest priority.
			}else {
				getUnit(activeIndex).setActivated(false) ;
				activeIndex = -1 ;
			}
		}

		if (activeIndex==-1) {
			for ( int i=0 ; i<nunits ; i++ ) {
				// the point maybe in several units' selection area,
				// but the last good unit will be choesen 
				// as the active one.
				if ( units[i].inSelectArea(x,y) ){
					if (activeIndex==-1) {
						activeIndex =  i ;
					} else {
						if(  !getUnit(i).isMovable() && 
							getUnit(activeIndex).isMovable() ) 
						{ // the movable unit should get higher priority than
						  // unit that activable but not movable
						} else {
							activeIndex = i ;					
						}
					}
	
				}
			}
		}
		if ( activeIndex!=-1) {
			getUnit(activeIndex).setActivated(true) ;
			unitX=getUnit(activeIndex).getX();
			unitY=getUnit(activeIndex).getY();
			lastX=me.getX();
			lastY=me.getY();
		}
		repaint();
	}
	
	public void mouseReleased(MouseEvent me) {
		int m,n;

			if (!dragBounds.contains(me.getX(),me.getY()) || 
				!dragBounds.contains(pressedX,pressedY ) ){
				status = WAIT;
				return ;
			}
		
		if ( (activeIndex==-1)) return ;
		
		if ( !getUnit(activeIndex).isMovable() ){
			getUnit(activeIndex).setActivated(false) ;
			repaint() ;
			return;
		} 
		m=me.getX()-lastX;
		n=me.getY()-lastY;
		unitX=unitX+m;
		unitY=unitY+n;
		
		// check if unit is dragged out of dragBounds.
		// this mechod call may change the values of unitX and unitY.
		checkDraggingPosition(activeIndex);
		
		status = WAIT ;
		if ( activeIndex!=-1) {
			getUnit(activeIndex).setPosition(unitX,unitY);
			repaint();
//			getUnit(activeIndex).setActivated(false) ;
		}
//		activeIndex = -1 ;
	}

	public void mouseEntered(MouseEvent me) {}
	public void mouseExited(MouseEvent me) {}
	public void mouseClicked(MouseEvent me) {
	}

	public void mouseMoved(MouseEvent me) {	}
	
	public void mouseDragged(MouseEvent me) {
		status = DRAG ;
		int m,n;
		if ( (activeIndex==-1)|| !getUnit(activeIndex).isMovable() ){
			return;
		}
		
		if( !dragBounds.contains(pressedX, pressedY ) ) {
			return ;
		}
	
		m=me.getX()-lastX;
		n=me.getY()-lastY;
		lastX=me.getX();
		lastY=me.getY();
	
		unitX=unitX+m;
		unitY=unitY+n;
		
		checkDraggingPosition(activeIndex) ;
		
		getUnit(activeIndex).setPosition(unitX,unitY);
		repaint();
	}//~mouseDragged
	
	private void checkDraggingPosition(int activeIndex) {
		if(unitX<dragBounds.x) unitX=dragBounds.x;
		if(unitY<dragBounds.y) unitY=dragBounds.y;
		if ( unitX>dragBounds.width-getUnit(activeIndex).getWidth() ){
				unitX = dragBounds.width-getUnit(activeIndex).getWidth() ;
		}
		if ( unitY>dragBounds.height-getUnit(activeIndex).getHeight() ){
				unitY = dragBounds.height-getUnit(activeIndex).getHeight();
		}
	}//~checkDraggingPosition

}//~DemoPane